﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using BLL;
using DAL.Managers;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using Opds_Viewer.Model;
using Opds_Viewer.Properties;

namespace Opds_Viewer.ViewModel
{
    public class CatalogViewModel : ViewModelBase
    {
        #region Private fields

        private MyCatalog _currentCatalog;
        private List<Ebook> _allBooks;
        private IEnumerable _oBooks;
        private Ebook _selectedBook;
        private BitmapImage _currentCover;
        private bool _isLoadingCover;
        private string _localFile;
        private string _breadcrumbs;
        private string _searchText;
        private string _filterType;

        // Commanding
        private readonly RelayCommand _loadBooksCommand;
        private readonly RelayCommand _loadCoverCommand;
        private readonly RelayCommand _markAllDownloadsCommand;
        private readonly RelayCommand<string> _markDownloadCommand;
        private readonly RelayCommand<Ebook> _markBookDownloadCommand;
        private readonly RelayCommand _filterCommand;

        #endregion

        #region constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="CatalogViewModel"/> class.
        /// </summary>
        public CatalogViewModel()
        {
            _loadBooksCommand = new RelayCommand(LoadBooks);
            _loadCoverCommand = new RelayCommand(LoadCover);
            _markAllDownloadsCommand = new RelayCommand(MarkAllDownloads);
            _markDownloadCommand = new RelayCommand<string>(MarkDownload);
            _markBookDownloadCommand = new RelayCommand<Ebook>(MarkBookDownload);
            _filterCommand = new RelayCommand(FilterBooks);
        }

        #endregion

        #region commanding

        public ICommand LoadBooksCommand
        {
            get { return _loadBooksCommand; }
        }

        public ICommand LoadCoverCommand
        {
            get { return _loadCoverCommand; }
        }

        public ICommand MarkAllDownloadsCommand
        {
            get { return _markAllDownloadsCommand; }
        }

        public ICommand MarkDownloadCommand
        {
            get { return _markDownloadCommand; }
        }

        public ICommand MarkBookDownloadCommand
        {
            get { return _markBookDownloadCommand; }
        }

        public ICommand FilterBookCommand
        {
            get { return _filterCommand; }
        }

        #endregion

        #region public properties

        /// <summary>
        /// Gets or sets the current catalog.
        /// </summary>
        /// <value>The selected catalog.</value>
        public MyCatalog CurrentCatalog
        {
            get { return _currentCatalog; }
            set
            {
                _currentCatalog = value;
                DAL.Connection.CatalogConnection = _currentCatalog.FileDb;
                RaisePropertyChanged("CurrentCatalog");
            }
        }

        /// <summary>
        /// Gets or sets the collection of books.
        /// </summary>
        /// <value>The books.</value>
        public IEnumerable OBooks
        {
            get { return _oBooks; }
            set
            {
                _oBooks = value;
                RaisePropertyChanged("OBooks");
            }
        }

        /// <summary>
        /// Gets or sets the selected book.
        /// </summary>
        /// <value>The selected book.</value>
        public Ebook SelectedBook
        {
            get { return _selectedBook; }
            set
            {
                _selectedBook = value;
                RaisePropertyChanged("SelectedBook");
            }
        }

        /// <summary>
        /// Gets or sets the current cover.
        /// </summary>
        /// <value>The current cover.</value>
        public BitmapImage CurrentCover
        {
            get { return _currentCover; }
            set
            {
                _currentCover = value;
                RaisePropertyChanged("CurrentCover");
            }
        }

        /// <summary>
        /// Gets or sets the indicator for loading the cover.
        /// </summary>
        public bool IsLoadingCover
        {
            get { return _isLoadingCover; }
            set
            {
                _isLoadingCover = value;
                RaisePropertyChanged("IsLoadingCover");
            }
        }

        /// <summary>
        /// Gets or sets the breadcrumbs.
        /// </summary>
        public string BreadCrumbs
        {
            get { return _breadcrumbs; }
            set
            {
                _breadcrumbs = value;
                RaisePropertyChanged("BreadCrumbs");
            }
        }

        /// <summary>
        /// The text filter for the ebook list
        /// </summary>
        public string SearchText
        {
            get { return _searchText; }
            set
            {
                _searchText = value;
                RaisePropertyChanged("SearchText");
            }
        }

        public string FilterType
        {
            get { return _filterType; }
            set
            {
                _filterType = value;
                RaisePropertyChanged("FilterType");
            }
        }

        #endregion

        #region public functions

        

        #endregion

        #region private functions

        private void LoadBooks()
        {
            _allBooks = EbookManager.Instance.GetAllBooks();

            // if it's the fisrt loading of the catalog,
            // or if the number of books is different from previous number
            // we stock it in the catalog.
            if (this.CurrentCatalog.NbBooks != _allBooks.Count)
            {
                this.CurrentCatalog.NbBooks = _allBooks.Count;
                this.CurrentCatalog.Save();
            }

            OBooks = _allBooks.ToList();
            SelectedBook = ((List<Ebook>)OBooks).FirstOrDefault();
            LoadCover();

            //init the breadcrumb
            this.BreadCrumbs = this.CurrentCatalog.Name + "'s catalog";
        }

        private void LoadCover()
        {
            // Todo : put default image
            CurrentCover = null;
            IsLoadingCover = true;

            try
            {
                // Get something like "Folders/cover.jpg"
                var coverUrl = SelectedBook.CoverUrl;

                // Check to see if the directory for caching has been created
                if (!Directory.Exists(Settings.Default.DirImageCacher))
                {
                    // Create it
                    Directory.CreateDirectory(Settings.Default.DirImageCacher);
                }

                // Construct the local path to the cache
                _localFile = String.Format("{0}/{1}", Settings.Default.DirImageCacher, coverUrl);

                // Check if the file exists
                if (!File.Exists(_localFile))
                {
                    // Create the subdirectories
                    var dirs = _localFile.Split('/');
                    if (dirs.Length > 2)
                    {
                        var path = dirs[0];
                        for (var i = 1; i < dirs.Length - 1; i++)
                        {
                            path = path + "/" + dirs[i];
                            if (!Directory.Exists(path))
                            {
                                // Create it
                                Directory.CreateDirectory(path);
                            }
                        }
                    }

                    // value[1] is the address of the database of the catalog
                    // like "http://.../something/metadata.db"
                    var catalog = CurrentCatalog.UrlDb;

                    //We just have to replace the metadata.db by the coverUrl
                    var cover = catalog.Substring(0, catalog.LastIndexOf("/") + 1) + coverUrl;

                    // Create the Bitmap and save it to the caching directory
                    CurrentCover = new BitmapImage(new Uri(cover));
                    CurrentCover.DownloadCompleted += DownloadCompleted;
                }
                else
                {
                    // Load from the local file
                    var path = Directory.GetCurrentDirectory() + "\\" + _localFile.Replace("/", "\\");
                    CurrentCover = new BitmapImage(new Uri(path));
                    IsLoadingCover = false;
                }
            }
            catch
            {
                // Just return the default cover
                IsLoadingCover = false;
            }
        }

        private void DownloadCompleted(object sender, EventArgs e)
        {
            var encoder = new JpegBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender));

            try
            {
                using (var filestream = new FileStream(_localFile, FileMode.Create))
                    encoder.Save(filestream);
            }
            catch
            {
                // just ignore it
            }
            finally
            {
                IsLoadingCover = false;
            }
        }

        private void MarkAllDownloads()
        {
            SelectedBook.Download = !SelectedBook.Download;
        }

        private void MarkDownload(string format)
        {
            var file = SelectedBook.Files.Where(f => f.Format == format).FirstOrDefault();
            if (file != null) file.Download = !file.Download;
        }

        private void MarkBookDownload(Ebook book)
        {
            
        }

        private void FilterBooks()
        {
            if (string.IsNullOrWhiteSpace(SearchText)) return;

            switch (FilterType)
            {
                case "Title":
                    OBooks = _allBooks.Where(e => RemoveDiacritics(e.Title).Contains(RemoveDiacritics(SearchText)));
                    break;
                case "Author":
                    OBooks = _allBooks.Where(e => RemoveDiacritics(e.Author.ToString()).Contains(RemoveDiacritics(SearchText)));
                    break;
                case "Serie":
                    OBooks = _allBooks.Where(e => e.Serie != null && RemoveDiacritics(e.Serie.Name).Contains(RemoveDiacritics(SearchText)));
                    break;
                default:
                    OBooks = _allBooks.Where(e => RemoveDiacritics(e.Title).Contains(RemoveDiacritics(SearchText)));
                    break;
            }

            SelectedBook = ((IEnumerable<Ebook>)OBooks).FirstOrDefault();
        }

        static string RemoveDiacritics(string text)
        {
            return string.Concat(
                text.Normalize(NormalizationForm.FormD)
                .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) !=
                                              UnicodeCategory.NonSpacingMark)
              ).Normalize(NormalizationForm.FormC);
        }

        #endregion
    }
}
